package com.ljl;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.File;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.util.Date;
import java.text.SimpleDateFormat;

public class Main extends JFrame implements ActionListener {

    private static final String INPUT_FILE_PATH_EMPTY_ERROR = "输入文件路径不能为空!";
    private static final String OUTPUT_FILE_PATH_EMPTY_ERROR = "输出文件路径不能为空!";
    private static final String INVALID_INPUT_FILE_PATH_ERROR = "无效的输入文件路径!";
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("H:mm:ss");
    private static final DateTimeFormatter TIME_INTERVAL_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");

    private JTextField inputFilePathField;
    private JTextField outputFilePathField;
    private JButton inputFileChooserButton;
    private JButton outputFileChooserButton;
    private JButton startButton;
    private JProgressBar progressBar;
    private int totalCsvFiles = 0;
    private int processedFiles = 0;

    // 修改：Key包含了文件名、时间段和价格
    private static class AggregateKey {
        String filename;
        String timeInterval;
        String price;

        public AggregateKey(String filename, String timeInterval, String price) {
            this.filename = filename;
            this.timeInterval = timeInterval;
            this.price = price;
        }

        // Important: Override equals and hashCode for HashMap to work correctly
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            AggregateKey that = (AggregateKey) o;
            return filename.equals(that.filename) &&
                    timeInterval.equals(that.timeInterval) &&
                    price.equals(that.price);
        }

        @Override
        public int hashCode() {
            int result = filename.hashCode();
            result = 31 * result + timeInterval.hashCode();
            result = 31 * result + price.hashCode();
            return result;
        }
    }

    // 修改：使用新的 AggregateKey
    private Map<AggregateKey, Integer> aggregatedData1;
    private Map<AggregateKey, Integer> aggregatedData2;
    private Map<AggregateKey, Integer> aggregatedData3;


    public Main() {
        super("数据处理");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Create components (unchanged)
        JLabel inputFileLabel = new JLabel("输入文件路径:");
        inputFilePathField = new JTextField(30);
        inputFileChooserButton = new JButton("选择文件夹");

        JLabel outputFileLabel = new JLabel("输出文件路径:");
        outputFilePathField = new JTextField(30);
        outputFileChooserButton = new JButton("选择文件夹");

        startButton = new JButton("开始处理");
        progressBar = new JProgressBar(0, 100);
        progressBar.setStringPainted(true);
        progressBar.setVisible(false);

        // Add action listeners (unchanged)
        inputFileChooserButton.addActionListener(this);
        outputFileChooserButton.addActionListener(this);
        startButton.addActionListener(this);

        // Layout using GridBagLayout (unchanged)
        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(5, 5, 5, 5);
        gbc.anchor = GridBagConstraints.WEST;

        // Input File Path (unchanged)
        gbc.gridx = 0;
        gbc.gridy = 0;
        add(inputFileLabel, gbc);

        gbc.gridx = 1;
        gbc.gridy = 0;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        add(inputFilePathField, gbc);

        gbc.gridx = 2;
        gbc.gridy = 0;
        gbc.weightx = 0.0;
        gbc.fill = GridBagConstraints.NONE;
        add(inputFileChooserButton, gbc);

        // Output File Path (unchanged)
        gbc.gridx = 0;
        gbc.gridy = 1;
        add(outputFileLabel, gbc);

        gbc.gridx = 1;
        gbc.gridy = 1;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        add(outputFilePathField, gbc);

        gbc.gridx = 2;
        gbc.gridy = 1;
        gbc.weightx = 0.0;
        gbc.fill = GridBagConstraints.NONE;
        add(outputFileChooserButton, gbc);

        // Progress Bar (unchanged)
        gbc.gridx = 0;
        gbc.gridy = 2;
        gbc.gridwidth = 3;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.anchor = GridBagConstraints.CENTER;
        add(progressBar, gbc);

        // Start Button (unchanged)
        gbc.gridx = 0;
        gbc.gridy = 3;
        gbc.gridwidth = 3;
        gbc.weightx = 0.0;
        gbc.fill = GridBagConstraints.NONE;
        gbc.anchor = GridBagConstraints.CENTER;
        add(startButton, gbc);

        // Set frame properties (unchanged)
        setSize(600, 200);
        setLocationRelativeTo(null);
        setVisible(true);

        // Initialize the aggregated data structures
        aggregatedData1 = new HashMap<>();
        aggregatedData2 = new HashMap<>();
        aggregatedData3 = new HashMap<>();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == inputFileChooserButton) {
            JFileChooser fileChooser = new JFileChooser();
            fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            int returnVal = fileChooser.showOpenDialog(this);
            if (returnVal == JFileChooser.APPROVE_OPTION) {
                File selectedFile = fileChooser.getSelectedFile();
                inputFilePathField.setText(selectedFile.getAbsolutePath());
            }
        } else if (e.getSource() == outputFileChooserButton) {
            JFileChooser fileChooser = new JFileChooser();
            fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            int returnVal = fileChooser.showOpenDialog(this);
            if (returnVal == JFileChooser.APPROVE_OPTION) {
                File selectedFile = fileChooser.getSelectedFile();
                outputFilePathField.setText(selectedFile.getAbsolutePath());
            }
        } else if (e.getSource() == startButton) {
            String inputFilePath = inputFilePathField.getText();
            String outputFilePath = outputFilePathField.getText();

            if (inputFilePath == null || inputFilePath.trim().isEmpty()) {
                JOptionPane.showMessageDialog(this, INPUT_FILE_PATH_EMPTY_ERROR, "错误", JOptionPane.ERROR_MESSAGE);
                return;
            }

            if (outputFilePath == null || outputFilePath.trim().isEmpty()) {
                JOptionPane.showMessageDialog(this, OUTPUT_FILE_PATH_EMPTY_ERROR, "错误", JOptionPane.ERROR_MESSAGE);
                return;
            }

            File inputFile = new File(inputFilePath);
            if (!inputFile.exists() || !inputFile.isDirectory()) {
                JOptionPane.showMessageDialog(this, INVALID_INPUT_FILE_PATH_ERROR, "错误", JOptionPane.ERROR_MESSAGE);
                return;
            }

            // Count CSV files
            totalCsvFiles = countCsvFiles(inputFile);

            // Start processing logic
            startProcessing(inputFilePath, outputFilePath);
        }
    }

    private int countCsvFiles(File directory) {
        int count = 0;
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isFile() && file.getName().toLowerCase().endsWith(".csv")) {
                    count++;
                } else if (file.isDirectory()) {
                    count += countCsvFiles(file);
                }
            }
        }
        return count;
    }

    private void startProcessing(String inputPath, String outputPath) {
        processedFiles = 0;
        SwingWorker<Void, String> worker = new SwingWorker<Void, String>() {
            @Override
            protected Void doInBackground() throws Exception {
                progressBar.setVisible(true);
                startButton.setEnabled(false);

                File inputDirectory = new File(inputPath);
                List<File> csvFiles = new ArrayList<>();
                getAllCsvFiles(inputDirectory, csvFiles); // Get all CSV files from directory and subdirectories

                //clear existing data before processing
                aggregatedData1.clear();
                aggregatedData2.clear();
                aggregatedData3.clear();

                int filesProcessed = 0;
                for (File csvFile : csvFiles) {
                    String filename = csvFile.getName().replaceFirst("[.][^.]+$", ""); // Remove file extension
                    try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) {
                        String line;
                        br.readLine(); // Skip header line
                        while ((line = br.readLine()) != null) {
                            String[] parts = line.split("[\t,]");
                            if (parts.length >= 3) {
                                try {
                                    String timeStr = parts[0].trim();
                                    String priceStr = parts[1].trim();
                                    String typeStr = parts[2].trim();

                                    // Only process if type is "H"
                                    if (typeStr.equals("H")) {
                                        // Parse the time using a DateTimeFormatter
                                        LocalTime time = LocalTime.parse(timeStr, TIME_FORMATTER);

                                        // Calculate the half-hour interval
                                        LocalTime intervalStart = time.truncatedTo(java.time.temporal.ChronoUnit.HOURS).plusMinutes((time.getMinute() / 30) * 30);
                                        LocalTime intervalEnd = intervalStart.plusMinutes(30).minusSeconds(1);
                                        String timeInterval = intervalStart.format(TIME_INTERVAL_FORMATTER) + "-" + intervalEnd.format(TIME_INTERVAL_FORMATTER);


                                        try {
                                            double price = Double.parseDouble(priceStr.replace("H", "")); // Handle "H" suffix

                                            // Create AggregateKey
                                            AggregateKey key = new AggregateKey(filename, timeInterval, priceStr); // Use raw priceStr

                                            // Aggregate data for all three categories
                                            if (price >= 6) {
                                                aggregatedData1.compute(key, (k, v) -> (v == null) ? 1 : v + 1);
                                            }
                                            if (price >= 50) {
                                                aggregatedData2.compute(key, (k, v) -> (v == null) ? 1 : v + 1);
                                            }
                                            if (price >= 500) {
                                                aggregatedData3.compute(key, (k, v) -> (v == null) ? 1 : v + 1);
                                            }
                                        } catch (NumberFormatException ex) {
                                            System.err.println("Error parsing price in file " + csvFile.getName() + ": " + ex.getMessage());
                                        }

                                    }
                                } catch (DateTimeParseException ex) {
                                    System.err.println("Error parsing time in file " + csvFile.getName() + ": " + ex.getMessage());
                                }
                            } else {
                                System.err.println("Skipping line in file " + csvFile.getName() + ": " + line);
                            }
                        }
                    } catch (IOException ex) {
                        System.err.println("Error reading file " + csvFile.getName() + ": " + ex.getMessage());
                    }

                    filesProcessed++;
                    int progress = (int) ((double) filesProcessed / totalCsvFiles * 100);
                    publish(String.format("已处理 %d / %d", filesProcessed, totalCsvFiles));

                }

                return null;
            }

            @Override
            protected void process(List<String> chunks) {
                String progressString = chunks.get(chunks.size() - 1);
                progressBar.setString(progressString);
                progressBar.setValue((int) ((double) processedFiles / totalCsvFiles * 100));
            }

            @Override
            protected void done() {
                progressBar.setVisible(false);
                startButton.setEnabled(true);

                try {
                    // Create Excel files
                    createExcelFile(outputPath, "output1", aggregatedData1, 7); // B >= 6, count >= 7
                    createExcelFile(outputPath, "output2", aggregatedData2, 10); // B >= 50, count >= 10
                    createExcelFile(outputPath, "output3", aggregatedData3, 3);  // B >= 500, count >= 3

                    JOptionPane.showMessageDialog(Main.this, "处理完成! Excel文件已生成.", "完成", JOptionPane.INFORMATION_MESSAGE);

                } catch (Exception ex) {
                    JOptionPane.showMessageDialog(Main.this, "Error during processing: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
                }
            }
        };

        worker.execute();
    }

    private void getAllCsvFiles(File directory, List<File> fileList) {
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isFile() && file.getName().toLowerCase().endsWith(".csv")) {
                    fileList.add(file);
                } else if (file.isDirectory()) {
                    getAllCsvFiles(file, fileList);
                }
            }
        }
    }

    private void createExcelFile(String outputPath, String baseFilename, Map<AggregateKey, Integer> data, int minCount) throws IOException {
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("Summary");

        // Create header row
        Row headerRow = sheet.createRow(0);
        headerRow.createCell(0).setCellValue("次数");
        headerRow.createCell(1).setCellValue("价格");
        headerRow.createCell(2).setCellValue("时间段");
        headerRow.createCell(3).setCellValue("文件名");

        int rowNum = 1;
        for (Map.Entry<AggregateKey, Integer> entry : data.entrySet()) {
            AggregateKey key = entry.getKey();
            int count = entry.getValue();

            if (count >= minCount) {
                Row row = sheet.createRow(rowNum++);
                row.createCell(0).setCellValue(count);
                row.createCell(1).setCellValue(key.price + "H"); // Add "H" back here.
                row.createCell(2).setCellValue(key.timeInterval);
                row.createCell(3).setCellValue(key.filename);
            }
        }
        //If there is no data, ensure the excel have headers for user to reference, and a single row with "No data to display" message
        if (rowNum == 1) {
            Row row = sheet.createRow(rowNum++);
            row.createCell(0).setCellValue("No data to display");
        }

        // Write the workbook to a file
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String timestamp = dateFormat.format(new Date());
        String outputFileName = baseFilename + "_" + timestamp + ".xlsx"; // Unique filename for each output.
        String outputFilePath = new File(outputPath, outputFileName).getAbsolutePath();

        try (FileOutputStream outputStream = new FileOutputStream(outputFilePath)) {
            workbook.write(outputStream);
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(Main.this, "Error writing Excel file: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
        } finally {
            try {
                workbook.close(); //VERY IMPORTANT: Close the workbook to prevent resource leaks.
            } catch (IOException e) {
                System.err.println("Error closing workbook: " + e.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Main());
    }
}